var CacheItem = function(value, tags) {
	this.value = value
	this.tags = tags
}

var Cache = function() {
	this.data = {}
	this.tags = {}
}

Cache.prototype = {
	/**
	 * Store value in cache
	 * @param <Object> value - value to store
	 * @param <String> key - key used to access value
	 * @param <Array | null | undefined> tags - tags to assign to the value. If not specified no tags are assigned
	 * @param <Number | null | undefined> lifetime - time in milliseconds before the value will be eliminated
	 */
	set : function(value, key, tags, lifetime) {

		//validate and normalize tags
		if(undefined === tags || null === tags) {
			tags = []
		}
		else if(typeof(tags) == 'string') {
			tags = [tags]
		}
		if(!(tags instanceof Array)) {
			throw ('Tags must be an array or a string (single tag)')
		}
		var vTags = []
		for (var i in tags) {
			vTags.push(tags[i].toString())
		}

		//validate and normalize key
		if(typeof(key) != 'string') {
			throw ('Key must be string')
		}
		
		var item = new CacheItem(value, vTags)
		this.data[key] = item
		for(var i in vTags) {
			var t = vTags[i]
			if(!this.tags[t]) {
				this.tags[t] = {}
			}
			this.tags[t][key] = item
		}
		
		if( !isNaN(lifetime) && lifetime > 0 ) {
			var cache = this
			window.setTimeout(function () {
				cache.clearKey(key)
			}, lifetime)
		}
	},
	
	/**
	 * Get the value
	 * @param <String> key
	 */
	get : function(key) {
		if(key in this.data) {
			return this.data[key].value
		}
	},
	
	/**
	 * Removes all the values with tag specified
	 * @param <String> tag
	 */
	clearTag : function(tag) {
		if(tag in this.tags){
			for (var key in this.tags[tag]) {
				this.clearKey(key)
			}
		}
	},

	/**
	 * Removes all the values
	 */
	clearAll : function() {
		this.tags = {}
		this.data = {}
	},
	
	/**
	 * Removes value with key specified
	 * @param <String> key
	 */
	clearKey : function(key) {
		var item = this.data[key]
		for (var i in item.tags) {
			var t = item.tags[i]
			
			//removing item from tag index
			delete this.tags[t][key]
			
			//removing tag if empty
			var empty = true
			for(var i in this.tags[t]) {
				empty = false
				break
			}
			if(empty) {
				delete this.tags[t]
			}
		}
		
		//removing item from key/value index
		delete this.data[key]
	}
}
